import { Block, Player, Vector3, world } from "@minecraft/server";
import { Game, GameComponent } from "@sapi-game/main";
import { ChickenHorseMainState } from "./state";
import { Vector3Utils } from "@sapi-game/utils";

export class PlayerSideJumpComponent extends GameComponent<ChickenHorseMainState> {
    override onAttach(): void {
        this.subscribe(Game.events.interval, () => {
            const players = world.getAllPlayers();
            players.forEach((p) => {
                const result = detectTouchingSides(p);
                if (result.length == 0) {
                    p.onScreenDisplay.setActionBar("未贴边");
                } else {
                    p.onScreenDisplay.setActionBar(
                        result.map((s) => s.side).join(",")
                    );
                }
                if (p.isJumping && result.length) {
                    applyImpulse(p, result);
                }
            });
        });
    }
}

function applyImpulse(player: Player, sides: TouchSide[]) {
    // 基础竖直冲量
    const vel = player.getVelocity();
    vel.y = 0.6;

    // 累积反方向向量
    const dir = { x: 0, z: 0 };

    for (const s of sides) {
        switch (s.side) {
            case "+X":
                vel.x = 0;
                dir.x -= 1;
                break;
            case "-X":
                vel.x = 0;
                dir.x += 1;
                break;
            case "+Z":
                vel.z = 0;
                dir.z -= 1;
                break;
            case "-Z":
                vel.z = 0;
                dir.z += 1;
                break;
        }
    }

    vel.x = vel.x * 0.8;
    vel.z = vel.z * 0.8;

    // 归一化方向，使水平总冲量恒定
    const len = Math.sqrt(dir.x * dir.x + dir.z * dir.z);
    if (len > 0) {
        const strength = 0.15; // 总水平冲量大小
        vel.x += (dir.x / len) * strength;
        vel.z += (dir.z / len) * strength;
    }

    player.clearVelocity();
    player.applyImpulse(vel);
}

type Side = "+X" | "-X" | "+Z" | "-Z";

interface TouchSide {
    side: Side;
    block: Block;
    blockPos: Vector3;
}

/**
 * 检测玩家是否贴着方块的任意侧面（不含上下）
 * @param player Player 对象
 * @param threshold 靠近边界的判定阈值（默认 0.32）
 * @param speedEps 静止判定速度阈值（默认 0.005）
 */
export function detectTouchingSides(
    player: Player,
    threshold: number = 0.32,
    speedEps: number = 0.005
): TouchSide[] {
    const dim = player.dimension;
    const vel = player.getVelocity();
    const pLoc = player.location;
    const headLoc = player.getHeadLocation();
    const posList = [
        pLoc,
        Vector3Utils.scale(Vector3Utils.add(pLoc, headLoc), 0.5),
        headLoc,
    ];
    const result: TouchSide[] = [];
    const addedSides = new Set<Side>();

    const directions = [
        { axis: "x" as const, posSide: "+X" as Side, negSide: "-X" as Side },
        { axis: "z" as const, posSide: "+Z" as Side, negSide: "-Z" as Side },
    ];

    for (const pos of posList) {
        const fx = pos.x - Math.floor(pos.x);
        const fz = pos.z - Math.floor(pos.z);
        const base = {
            x: Math.floor(pos.x),
            y: Math.floor(pos.y),
            z: Math.floor(pos.z),
        };

        for (const { axis, posSide, negSide } of directions) {
            const f = axis === "x" ? fx : fz;
            const v = axis === "x" ? vel.x : vel.z;

            const nearNeg = f < threshold;
            const nearPos = f > 1 - threshold;

            if (
                (nearNeg && (Math.abs(v) < speedEps || v < 0)) ||
                (nearPos && (Math.abs(v) < speedEps || v > 0))
            ) {
                const side = nearNeg ? negSide : posSide;
                if (addedSides.has(side)) continue;

                const blockPos = { ...base };
                blockPos[axis] += nearNeg ? -1 : 1;

                const block = dim.getBlock(blockPos);
                if (!block || block.isAir) continue;

                result.push({ side, block, blockPos });
                addedSides.add(side);
            }
        }
    }

    return result;
}
